package com.husd.framework.apiDocProductAdapter.allegro;

import org.apache.commons.lang3.StringUtils;
import sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl;

import java.io.File;
import java.io.IOException;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.util.ArrayList;
import java.util.List;
import java.util.Stack;

/**
 * @author shengdonghu@126.com
 * @date 2023/10/11 14:24
 */
public class ProductAdapterAutoCodeUtil {

    String mapEnumName;
    String finalClassName;

    public ProductAdapterAutoCodeUtil(String mapEnumName, String finalClassName) {
        this.mapEnumName = mapEnumName;
        this.finalClassName = finalClassName;
    }

    static Stack<AutoCodeMethod> methodStack = new Stack<>();

    public void start(Class reqClass) {

        String className = reqClass.getName();
        String simpleClassName = reqClass.getSimpleName();
        StringBuilder sb = new StringBuilder();
        sb.append("package com.vevor.open.allegro.service.product;\n");
        sb.append("import com.vevor.open.sdk.allegro.domain.product." + simpleClassName + ";\n");
        sb.append("import com.vevor.prm.domain.*;\n");
        sb.append("import com.alibaba.fastjson.JSONObject;\n");

        sb.append("import java.util.ArrayList;\n");
        sb.append("import java.util.List;\n");
        sb.append("import org.apache.commons.lang3.StringUtils;\n" +
                "\n" +
                "import java.util.HashMap;\n" +
                "import java.util.List;\n" +
                "import java.util.Map;\n");

        sb.append("public class " + finalClassName + " {\n");

        sb.append("public " + className + " convert(PublishInfo publishInfo) { \n");
        sb.append(" " + className + " res = new " + className + "();\n");

        // 从栈里拿到所有的方法 递归生成方法
        String code1 = geneMethodStack(reqClass);

        sb.append(code1).append("\n");
        sb.append("return res;\n");
        sb.append("}\n");
        StringBuilder methodS = geneMethodList();
        sb.append(methodS);

        String p2 = firstLower(mapEnumName);
        // 固定方法
        sb.append(" private String convertFieldsFromPublishInfo(PublishInfo publishInfo, " + mapEnumName + " " + p2 + ") {\n" +
                "\n" +
                "        String publishInfoField = " + p2 + ".getPublishInfoField();\n" +
                "        switch (publishInfoField) {\n" +
                "            case \"attrList\":\n" +
                "                // 这里固定写死为返回String 实际需要根据\n" +
                "                PublishAttr publishAttr = getFromList(publishInfo.getAttrList(), " + p2 + ".getKey());\n" +
                "                return publishAttr.getAttrValueString();\n" +
                "            case \"extendList\":\n" +
                "                PublishAttr extendList = getFromList(publishInfo.getExtendList(), " + p2 + ".getKey());\n" +
                "                return extendList.getAttrValueString();\n" +
                "            case \"baseInfoList\":\n" +
                "                PublishBaseInfo publishBaseInfo = publishInfo.getBaseInfoList().get(0);\n" +
                "                String key = " + p2 + ".getKey();\n" +
                "                switch (key) {\n" +
                "                    case \"skuCode\":\n" +
                "                        return publishBaseInfo.getSkuCode();\n" +
                "                    case \"eanCode\":\n" +
                "                        return publishBaseInfo.getEanCode();\n" +
                "                    case \"name1\":\n" +
                "                        return publishBaseInfo.getName1();\n" +
                "                    case \"name2\":\n" +
                "                        return publishBaseInfo.getName2();\n" +
                "                    case \"name3\":\n" +
                "                        return publishBaseInfo.getName3();\n" +
                "                    case \"name4\":\n" +
                "                        return publishBaseInfo.getSkuCode();\n" +
                "                    case \"description\":\n" +
                "                        return publishBaseInfo.getDescription();\n" +
                "//                    case \"length\":\n" +
                "//                        return publishBaseInfo.getLength();\n" +
                "//                    case \"width\":\n" +
                "//                        return publishBaseInfo.getWidth();\n" +
                "//                    case \"height\":\n" +
                "//                        return publishBaseInfo.getHeight();\n" +
                "//                    case \"length1\":\n" +
                "//                        return publishBaseInfo.getLength1();\n" +
                "//                    case \"length2\":\n" +
                "//                        return publishBaseInfo.getLength2();\n" +
                "//                    case \"length3\":\n" +
                "//                        return publishBaseInfo.getLength3();\n" +
                "//                    case \"length4\":\n" +
                "//                        return publishBaseInfo.getLength4();\n" +
                "//                    case \"length5\":\n" +
                "//                        return publishBaseInfo.getLength5();\n" +
                "//                    case \"length6\":\n" +
                "//                        return publishBaseInfo.getLength6();\n" +
                "//                    case \"grossWeight\":\n" +
                "//                        return publishBaseInfo.getGrossWeight();\n" +
                "//                    case \"netWeight\":\n" +
                "//                        return publishBaseInfo.getNetWeight();\n" +
                "//                    case \"weight1\":\n" +
                "//                        return publishBaseInfo.getWeight1();\n" +
                "//                    case \"weight2\":\n" +
                "//                        return publishBaseInfo.getWeight2();\n" +
                "//                    case \"weight3\":\n" +
                "//                        return publishBaseInfo.getWeight3();\n" +
                "//                    case \"weight4\":\n" +
                "//                        return publishBaseInfo.getWeight4();\n" +
                "//                    case \"weight5\":\n" +
                "//                        return publishBaseInfo.getWeight5();\n" +
                "//                    case \"weight6\":\n" +
                "//                        return publishBaseInfo.getWeight6();\n" +
                "                    case \"brandId\":\n" +
                "                        return publishBaseInfo.getBrandId();\n" +
                "                    case \"brandName\":\n" +
                "                        return publishBaseInfo.getBrandName();\n" +
                "                    case \"extendMsg\":\n" +
                "                        return publishBaseInfo.getExtendMsg();\n" +
                "                }\n" +
                "            default:\n" +
                "                return null;\n" +
                "        }\n" +
                "    } \n ");

        sb.append("    private PublishAttr getFromList(List<PublishAttr> list, String attrName) {\n" +
                "\n" +
                "        for (PublishAttr publishAttr : list) {\n" +
                "            if (StringUtils.equalsIgnoreCase(attrName, publishAttr.getAttrName())) {\n" +
                "                return publishAttr;\n" +
                "            }\n" +
                "        }\n" +
                "        return null;\n" +
                "    }\n");

        sb.append("    private List<String> convertFieldsFromPublishInfoAttrList(PublishInfo publishInfo,  " + mapEnumName + "  " + p2 + ") {\n" +
                "\n" +
                "        String publishInfoField = " + p2 + ".getPublishInfoField();\n" +
                "        switch (publishInfoField) {\n" +
                "            case \"attrList\":\n" +
                "                // 这里固定写死为返回String 实际需要根据\n" +
                "                PublishAttr publishAttr = getFromList(publishInfo.getAttrList(), " + p2 + ".getKey());\n" +
                "                String valueArray3 = publishAttr.getAttrValueString();\n" +
                "                List<String> jsonArray = JSONObject.parseArray(valueArray3, String.class);\n" +
                "                return jsonArray;\n" +
                "            default:\n" +
                "                return null;\n" +
                "        }\n" +
                "    } \n");


        sb.append("}\n");
        write2File("/Users/hushengdong/hushengdong/git-vevor/open-admin/open-allegro/open-allegro-service/src/main/java/com/vevor/open/allegro/service/product/" + finalClassName + ".java", sb.toString());
        // 写入文件
        // System.out.println(sb.toString());
    }

    private String geneMethodNameByJsonPath(boolean isList, String jsonPath) {

        String[] arr = jsonPath.split(":");
        StringBuilder ss = new StringBuilder();
        ss.append("convertFields");
        for (int i = 0; i < arr.length; i++) {
            String str = arr[i];
            str = str.replaceAll("-", "");
            ss.append(firstUpper(str));
        }
        ss.append("FromPublishInfo");
        return ss.toString();
    }

    // 生成这个类的set语句 并且把set方法依赖的方法 加入到站中
    // 如果这个类本身已经是String 这种基本类型了 就需要单独处理
    private String geneMethodStack(Class clz) {

        StringBuilder res = new StringBuilder();
        boolean clzPrimitive = baseType(clz);
        if (clzPrimitive) {

        }


        Field[] fieldArray = clz.getDeclaredFields();
        // 生成
        for (Field f : fieldArray) {

            Class cc = f.getType();
            // 判断它是不是一个复杂类
            boolean primitive = baseType(cc);

            boolean isList = fieldIsList(f);

            // 基本类型 放到methodList 直接生成对应的类
            OpenJsonPath openJsonPath = f.getAnnotation(OpenJsonPath.class);
            String methodName = geneMethodNameByJsonPath(isList, openJsonPath.path());
            String fName = f.getName();

            if (primitive) {

                List<String> params = new ArrayList<>();
                params.add("PublishInfo");
                String path = openJsonPath.path();
                OpenSdkAllegroCreateProductOffersReqMapEnum productAdapterEnum = findProductAdapterEnumByPath(path);
                if (productAdapterEnum == null) {
                    throw new RuntimeException("没有在枚举类 OpenSdkAllegroCreateProductOffersReqMapEnum 找到 path:" + path + " ");
                }
                params.add(mapEnumName);
                AutoCodeMethod autoCodeMethod = new AutoCodeMethod(f, methodName, params);
                methodStack.push(autoCodeMethod);

                res.append("res.set").append(firstUpper(fName)).append("(")
                        .append(isList ? autoCodeMethod.getMethodName4List() : autoCodeMethod.getMethodName())
                        .append(setMethodParamsList(productAdapterEnum))
                        .append("\n");
            } else {

                // 复杂类 就需要递归处理
                List<String> params = new ArrayList<>();
                params.add("PublishInfo");

                String path = openJsonPath.path();
                OpenSdkAllegroCreateProductOffersReqMapEnum productAdapterEnum = findProductAdapterEnumByPath(path);
                if (productAdapterEnum == null) {
                    throw new RuntimeException("没有在枚举类 OpenSdkAllegroCreateProductOffersReqMapEnum 找到 path:" + path + " ");
                }
                params.add(mapEnumName);

                AutoCodeMethod autoCodeMethod = new AutoCodeMethod(f, methodName, params);
                res.append("res.set").append(firstUpper(fName)).append("(").
                        append(isList ? autoCodeMethod.getMethodName4List() : autoCodeMethod.getMethodName()).
                        append(setMethodParamsList(productAdapterEnum)).append("\n");
                methodStack.push(autoCodeMethod);

            }
        }
        return res.toString();
    }

    private String setMethodParamsList(OpenSdkAllegroCreateProductOffersReqMapEnum productOffersReqMapEnum) {

        String str = "(publishInfo," + mapEnumName + "." + productOffersReqMapEnum.name() + "));";
        return str;
    }

    private StringBuilder geneMethodList() {

        StringBuilder methodS = new StringBuilder();
        while (methodStack.isEmpty() == false) {
            AutoCodeMethod autoCodeMethod = methodStack.pop();
            StringBuilder sss = geneMethod(autoCodeMethod);
            methodS.append(sss).append("\n");
        }
        return methodS;
    }

    private StringBuilder geneMethod(AutoCodeMethod autoCodeMethod) {

        Field f = autoCodeMethod.getReturnClass();
        Class cc = f.getType();
        // 判断它是不是一个复杂类
        boolean primitive = baseType(cc);
        if (primitive) {
            return geneMethodSimple(autoCodeMethod, null);
        } else {
            // 需要重新生成这个复杂类的设置方法 这里不正确 需要改 不能直接调
            StringBuilder stringBuilder = new StringBuilder();
            boolean isList = fieldIsList(f);
            if (isList) {
                // 如果是List 需要生成2个方法
                String newMethodName = autoCodeMethod.getMethodName4List();
                String returnClassName = returnClassNameFromListField(f);
                stringBuilder.append(geneMethodOfList(autoCodeMethod, newMethodName, returnClassName));
                // List的实际类型 可能是复杂类型 也可能是简单类型 需要区分一下
                Type returnClassType = returnClassTypeFromListField(f);
                boolean pri = AutoCodeUtil.isBaseType(returnClassType.getTypeName());
                if (pri) {
                    stringBuilder.append(geneMethodSimple(autoCodeMethod, returnClassType.getTypeName()));
                } else {
                    stringBuilder.append(geneMethodComplex(autoCodeMethod, returnClassName));
                }
            } else {
                stringBuilder.append(geneMethodComplex(autoCodeMethod, null));
            }

            return stringBuilder;
        }
    }

    private String returnClassNameFromListField(Field field) {

        String clz = ((ParameterizedTypeImpl) field.getGenericType()).getActualTypeArguments()[0].getTypeName();
        String returnClassName = clz.replaceAll("\\$", ".");
        System.out.println("xxxxxx list: " + returnClassName);
        return returnClassName;
    }

    private Type returnClassTypeFromListField(Field field) {

        Type type = ((ParameterizedTypeImpl) field.getGenericType()).getActualTypeArguments()[0];

        return type;
    }


    private String returnClassNameFromField(Field field) {

        String clz = field.getType().getName();
        String returnClassName = clz.replaceAll("\\$", ".");
        System.out.println("xxxxxx: " + returnClassName);
        return returnClassName;
    }

//    private boolean classIsList(Class cc) {
//
//        String returnClassFullName = cc.getName();
//        returnClassFullName = returnClassFullName.replaceAll("\\$", ".");
//
//        boolean isList = StringUtils.equalsIgnoreCase(returnClassFullName, "java.util.List");
//        return isList;
//    }

    private boolean fieldIsList(Field f) {

        return f.getType() == List.class || f.getType() == Array.class;
    }

    private StringBuilder geneMethodOfList(AutoCodeMethod autoCodeMethod, String methodName4List, String returnClassFullName) {

        Field f = autoCodeMethod.getReturnClass();

        StringBuilder sb = new StringBuilder();
        sb.append("public List<").append(returnClassFullName).append("> ").append(methodName4List).append("(");

        List<String> params = autoCodeMethod.getParams();
        for (int i = 0; i < params.size(); i++) {
            String ppp = params.get(i);
            sb.append(ppp).append(" ").append(geneParamName(ppp));
            boolean lastOne = (i == params.size() - 1);
            if (!lastOne) {
                sb.append(",");
            }
        }
        sb.append(")");
        sb.append("{\n");

        String t = "List<%s> res = new ArrayList(); \n ";
        sb.append(String.format(t, returnClassFullName));
        sb.append("res.add(");
        // arrayList 需要单独处理
        OpenJsonPath openJsonPath = f.getAnnotation(OpenJsonPath.class);
        String path = openJsonPath.path();
        OpenSdkAllegroCreateProductOffersReqMapEnum productAdapterEnum = findProductAdapterEnumByPath(path);
        if (productAdapterEnum == null) {
            throw new RuntimeException("没有在枚举类 OpenSdkAllegroCreateProductOffersReqMapEnum 找到 path:" + path + " ");
        }
        String methodName = geneMethodNameByJsonPath(false, openJsonPath.path());
        sb.append(methodName).append(setMethodParamsList(productAdapterEnum)).append("\n");

        sb.append("return res; \n");
        sb.append("}\n");
        return sb;
    }

    private StringBuilder geneMethodComplex(AutoCodeMethod autoCodeMethod, String returnClassName) {

        Field f = autoCodeMethod.getReturnClass();
        Class cc = f.getType();
        String returnClassFullName = null;
        if (returnClassName == null) {
            returnClassFullName = returnClassNameFromField(f);
        } else {
            returnClassFullName = returnClassName;
        }

        StringBuilder sb = new StringBuilder();

        sb.append("public ").append(returnClassFullName).append(" ").append(autoCodeMethod.getMethodName()).append("(");

        List<String> params = autoCodeMethod.getParams();
        for (int i = 0; i < params.size(); i++) {
            String ppp = params.get(i);
            sb.append(ppp).append(" ").append(geneParamName(ppp));
            boolean lastOne = (i == params.size() - 1);
            if (!lastOne) {
                sb.append(",");
            }
        }
        sb.append(")");
        sb.append("{\n");

        String t = "%s res = new %s(); \n ";
        sb.append(String.format(t, returnClassFullName, returnClassFullName));
        boolean isList = fieldIsList(f);
        String setStatements = isList ? geneMethodStack(actualClassFromField(f)) : geneMethodStack(cc);
        sb.append(setStatements);

        sb.append("return res;\n");
        sb.append("}\n");
        return sb;
    }

    private Class actualClassFromField(Field f) {

        if (f.getType() == List.class) {
            // 如果是List类型，得到其Generic的类型
            Type genericType = f.getGenericType();
            if (genericType == null) {
                throw new RuntimeException("f name:" + f.getName() + " 是List 但是没有指定范型类型");
            }
            ;
            // 如果是泛型参数的类型
            if (genericType instanceof ParameterizedType) {
                ParameterizedType pt = (ParameterizedType) genericType;
                //得到泛型里的class类型对象
                Class<?> genericClazz = (Class<?>) pt.getActualTypeArguments()[0];
                return genericClazz;
            }
        }
        throw new RuntimeException("f name:" + f.getName() + " 获取范型类型异常");
    }

    private String geneParamName(String ppp) {

        return firstLower(ppp);
    }

    private StringBuilder geneMethodSimple(AutoCodeMethod autoCodeMethod, String newReturnClassName) {

        Field f = autoCodeMethod.getReturnClass();
        Class cc = f.getType();
        String returnClassFullName = null;
        if (StringUtils.isBlank(newReturnClassName)) {
            returnClassFullName = cc.getName();
        } else {
            returnClassFullName = newReturnClassName;
        }
        StringBuilder sb = new StringBuilder();

        sb.append("public ").append(returnClassFullName).append(" ").append(autoCodeMethod.getMethodName()).append("(");
        List<String> params = autoCodeMethod.getParams();

        StringBuilder paramList = new StringBuilder();
        for (int i = 0; i < params.size(); i++) {
            String ppp = params.get(i);
            sb.append(ppp).append(" ").append(geneParamName(ppp));
            paramList.append(geneParamName(ppp));
            boolean lastOne = (i == params.size() - 1);
            if (!lastOne) {
                sb.append(",");
                paramList.append(",");
            }
        }
        sb.append(")");
        sb.append("{\n");

//        String t = "%s res = new %s(); \n ";
//        sb.append(String.format(t, returnClassFullName, returnClassFullName));
//
//        sb.append("return res");
        sb.append("return convertFieldsFromPublishInfo(").append(paramList.toString()).append(");\n");
        sb.append("}\n");
        return sb;
    }


    private boolean baseType(Class c) {

        String name = c.getName();
        if (StringUtils.isNotBlank(name) && name.startsWith("java.lang")) {
            return true;
        }
        if (StringUtils.isNotBlank(name) && name.startsWith("java.math")) {
            return true;
        }
        return c.isPrimitive() || c.isAssignableFrom(Number.class);
    }

    private static OpenSdkAllegroCreateProductOffersReqMapEnum findProductAdapterEnumByPath(String path) {

        for (OpenSdkAllegroCreateProductOffersReqMapEnum item : OpenSdkAllegroCreateProductOffersReqMapEnum.values()) {

            if (StringUtils.equalsIgnoreCase(path, item.getPlatformPath())) {
                return item;
            }
        }
        return null;
    }

    private String firstUpper(String str) {

        char[] arr = str.toCharArray();
        arr[0] = Character.toUpperCase(arr[0]);
        return String.valueOf(arr);
    }

    private String firstLower(String str) {

        char[] arr = str.toCharArray();
        arr[0] = Character.toLowerCase(arr[0]);
        return String.valueOf(arr);
    }

    public void write2File(String path, String content) {

        try {
            File f = new File(path);
            if (!f.exists()) {
                f.getParentFile().mkdirs();
            }
            Path p = Paths.get(path);
            Files.deleteIfExists(p);
            Files.createFile(p);
            Files.write(p, content.getBytes(StandardCharsets.UTF_8), StandardOpenOption.TRUNCATE_EXISTING);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
